/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mobdev.notepad.shared.ui;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

import com.mobdev.notepad.shared.DBConstants;
import com.mobdev.notepad.shared.NotepadStrings;
import com.mobdev.notepad.shared.UtilityMethods;

/**
 * A generic activity for editing a note in a database.  This can be used
 * either to simply view a note {@link Intent#ACTION_VIEW}, view and edit a note
 * {@link Intent#ACTION_EDIT}, or create a new note {@link Intent#ACTION_INSERT}.
 */
public abstract class AbstractNoteEditor extends Activity
{
    private static final String TAG = "Notes";

    /**
     * Standard projection for the interesting columns of a normal note.
     */
    private static final String[] PROJECTION = new String[] {
            DBConstants._ID, // 0
            DBConstants.TITLE, // 1
            DBConstants.CONTENTS, // 1
            DBConstants.MODIFIED_ON, // 2
    };

    /** The index of the note column */
    private static final int COLUMN_INDEX_TITLE = 1;
    private static final int COLUMN_INDEX_NOTE = 2;
    private static final int COLUMN_INDEX_MODIFIED_ON = 3;

    // This is our state data that is stored when freezing.
    private static final String ORIGINAL_TITLE = "origTitle";
    private static final String ORIGINAL_CONTENT = "origContent";
    private static final String ORIGINAL_MODIFIED_TIME = "origModified";

    // Identifiers for our menu items.
    private static final int REVERT_ID = Menu.FIRST;
    private static final int DISCARD_ID = Menu.FIRST + 1;
    private static final int DELETE_ID = Menu.FIRST + 2;
    private static final int EDIT_TITLE_ID = Menu.FIRST + 3;
    private static final int SYNC_ID = Menu.FIRST + 4;

    // The different states this activity can be run in.
    private static final int STATE_EDIT = 0;
    private static final int STATE_INSERT = 1;

    private int mState;
    private Uri mUri;
    private Cursor mCursor;
    protected EditText mText;

    private String mOriginalTitle;
    private String mOriginalContent;
    private Integer mOriginalModifiedTime;

    private String mPreviouslySavedContent;

    private boolean cancelNote = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Intent intent = getIntent();

        // Do some setup based on the action being performed.

        final String action = intent.getAction();
        if (Intent.ACTION_EDIT.equals(action)) {
            // Requested to edit: set that state, and the data being edited.
            mState = STATE_EDIT;
            mUri = intent.getData();
        } else if (Intent.ACTION_INSERT.equals(action)) {
            // Requested to insert: set that state, and create a new entry
            // in the container.
            mState = STATE_INSERT;
            mUri = getContentResolver().insert(intent.getData(), null);

            // If we were unable to create a new note, then just finish
            // this activity.  A RESULT_CANCELED will be sent back to the
            // original activity if they requested a result.
            if (mUri == null) {
                Log.e(TAG, "Failed to insert new note into " + getIntent().getData());
                finish();
                return;
            }

            // The new entry was created, so assume all will end well and
            // set the result to be returned.
            setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));

        } else {
            // Whoops, unknown action!  Bail.
            Log.e(TAG, "Unknown action, exiting");
            finish();
            return;
        }

        init();

        // If an instance of this activity had previously stopped, we can
        // get the original text it started with.
        if (savedInstanceState != null) {
        	mOriginalTitle = savedInstanceState.getString(ORIGINAL_TITLE);
            mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT);
            mOriginalModifiedTime = savedInstanceState.getInt(ORIGINAL_MODIFIED_TIME);
        }
    }

    protected abstract void init();
        //findViewById(R.id.note)


	protected abstract int getSyncMenuIconId();
    //R.drawable.sync_48


	protected abstract void doSync();


	protected abstract void doEditTitle(Uri mUri);



	@Override
    protected void onResume() {
        super.onResume();


        // Get the note!
        mCursor = managedQuery(mUri, PROJECTION, null, null, null);


        // If we didn't have any trouble retrieving the data, it is now
        // time to get at the stuff.
        if (mCursor != null) {

        	//make sure the note still exists. This is needed for when the note is deleted during the sync process because it was deleted on the server.
            if(mCursor.getCount() == 0) {
            	finish(); //terminate this activity and return
            	return;
            }

            // Make sure we are at the one and only row in the cursor.
            mCursor.moveToFirst();



            // Modify our overall title depending on the mode we are running in.
            if (mState == STATE_EDIT) {
                setTitle(NotepadStrings.title_edit);
            } else if (mState == STATE_INSERT) {
                setTitle(NotepadStrings.title_create);
            }

            // This is a little tricky: we may be resumed after previously being
            // paused/stopped.  We want to put the new text in the text view,
            // but leave the user where they were (retain the cursor position
            // etc).  This version of setText does that for us.
            String title = mCursor.getString(COLUMN_INDEX_TITLE);
            String note = mCursor.getString(COLUMN_INDEX_NOTE);
            mText.setTextKeepState(note);

            mPreviouslySavedContent = note;


            // If we hadn't previously retrieved the original text, do so
            // now.  This allows the user to revert their changes.
            if (mOriginalTitle == null || mOriginalContent == null || mOriginalModifiedTime == null)
            {
                mOriginalTitle = title;
            	mOriginalContent = note;
                mOriginalModifiedTime = mCursor.getInt(COLUMN_INDEX_MODIFIED_ON);
            }

        } else {
            setTitle(NotepadStrings.error_title);
            mText.setText(NotepadStrings.error_message);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState)
    {
        // Save away the original text, so we still have it if the activity
        // needs to be killed while paused.
    	outState.putString(ORIGINAL_TITLE, mOriginalTitle);
        outState.putString(ORIGINAL_CONTENT, mOriginalContent);
        outState.putInt(ORIGINAL_MODIFIED_TIME, mOriginalModifiedTime);
    }

    @Override
    protected void onPause() {
        super.onPause();


        if( mCursor != null)
        {
    		saveNote();
			mCursor.close();
			mCursor = null;
        }
    }

	/**
	 * Save or create the note.
	 */
	protected void saveNote()
	{
		String text = mText.getText().toString();
		int length = text.length();

		// If we are creating a new note, then we want to also create
		// an initial title for it.
		if (mState == STATE_INSERT) {

			// If this activity is finished, and there is no text, then we
			// do something a little special: simply delete the note entry.
			// Note that we do this both for editing and inserting...  it
			// would be reasonable to only do it when inserting.
			if (isFinishing() && (length == 0 || cancelNote))
			{
				setResult(RESULT_CANCELED);
				deleteNote();
			}
			else
			{
				//compute the title for the note
				ContentValues values = new ContentValues();

				String title = null;
				int idxOfEndl = text.indexOf('\n');
				if(idxOfEndl > 0 && idxOfEndl < 50)
				{
					title = text.substring(0, idxOfEndl);
				}
				else if(length > 0 && length < 50)
				{
					title = text;
				}
				else if(length > 0)
				{
					title = text.substring(0, Math.min(length, 50)) + "...";
				}
				else
				{
					title = "Untitled";
				}

				//save the note
				int modifiedTime =  (int) ( System.currentTimeMillis()/1000 );
				values.put(DBConstants.TITLE, title);
				values.put(DBConstants.CONTENTS, text);
				values.put(DBConstants.MODIFIED_ON, modifiedTime);
				getContentResolver().update(mUri, values, null, null);

				mOriginalTitle = title;
            	mOriginalContent = text;
                mOriginalModifiedTime = modifiedTime;
				mState = STATE_EDIT;
			}
		}
		else if(!text.equals(mPreviouslySavedContent) || cancelNote) //if the contents changed
		{
			// Get changes into the provider.
			ContentValues values = new ContentValues();
			if(cancelNote) {
				values.put(DBConstants.TITLE, mOriginalTitle);
				values.put(DBConstants.CONTENTS, mOriginalContent);
				values.put(DBConstants.MODIFIED_ON, mOriginalModifiedTime);
			} else {
				values.put(DBConstants.CONTENTS, text);
				values.put(DBConstants.MODIFIED_ON, System.currentTimeMillis()/1000);
			}

			// Commit all of our changes to persistent storage. When the update completes
			// the content provider will notify the cursor of the change, which will
			// cause the UI to be updated.
			getContentResolver().update(mUri, values, null, null);
		}
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);

		menu.clear();
		menu.add(0, SYNC_ID, 0, NotepadStrings.menu_sync)
			.setShortcut('1', 's')
			.setIcon(getSyncMenuIconId());

	    menu.add(0, EDIT_TITLE_ID, 0, NotepadStrings.menu_edit_title)
			.setShortcut('2', 't')
			.setIcon(android.R.drawable.ic_menu_edit);


	    // Build the menus that are shown when editing.
	    if (mState == STATE_EDIT) {
	        menu.add(0, REVERT_ID, 0, NotepadStrings.menu_revert)
	                .setShortcut('3', 'r')
	                .setIcon(android.R.drawable.ic_menu_revert);

	        menu.add(0, DELETE_ID, 0, NotepadStrings.menu_delete)
	                .setShortcut('4', 'd')
	                .setIcon(android.R.drawable.ic_menu_delete);

	    // Build the menus that are shown when inserting.
	    } else {
	        menu.add(0, DISCARD_ID, 0, NotepadStrings.menu_discard)
	                .setShortcut('3', 'd')
	                .setIcon(android.R.drawable.ic_menu_delete);
	    }


	    return true;

	}



	@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle all of the possible menu actions.
        switch (item.getItemId()) {
        case DELETE_ID:
            deleteNote();
            finish();
            break;
        case DISCARD_ID:
            cancelNote();
            break;
        case REVERT_ID:
            cancelNote();
            break;
        case EDIT_TITLE_ID:
        	doEditTitle(mUri);
        	break;
        case SYNC_ID:
			doSync();
            break;

        }
        return super.onOptionsItemSelected(item);
    }



    /**
     * Take care of canceling work on a note.  Deletes the note if we
     * had created it, otherwise reverts to the original text.
     */
    private final void cancelNote() {
        cancelNote = true;
        setResult(RESULT_CANCELED);
        finish();
    }

    /**
     * Take care of deleting a note.  Simply deletes the entry.
     */
    private final void deleteNote() {
        if (mCursor != null) {
        	if (mState == STATE_EDIT) {
        		UtilityMethods.markNoteForDeletion(this, mUri);
        	} else if (mState == STATE_INSERT) {
        		getContentResolver().delete(mUri, null, null);
        	}
            mText.setText("");
        }
    }
}
